#ifndef __TOYM__BASE__LOGGER_H__
#define __TOYM__BASE__LOGGER_H__

#include <string>
#include <cstdio>
#include "Base/FixedBuffer.h"
#include "Base/Callables.h"

namespace toym
{

// class Logger: public Singleton<Logger> {
// public:
//     void putLog(const std::string& log) {
//         m_logQueue.push(log);
//     }
//     void putLog(std::string&& log) {
//         m_logQueue.push(std::move(log));
//     }

//     std::string getLog() {
//         return m_logQueue.popAndGet();
//     }

// private:
//     Logger()
//         : m_logQueue()
//     {}

//     BlockingQueue<std::string> m_logQueue;
// };


class LogPuter {
public:
    enum class LogLevel { DEBUG, INFO, WARNING, ERROR, CRITICAL, nLogLevel };

    LogPuter(const char* fileName, int line, LogLevel level = LogLevel::INFO);
    ~LogPuter();

    static
    LogLevel getLevel() {
        return s_level;
    }

    static
    void setLevel(LogLevel logLevel) {
        s_level = logLevel;
    }

    static
    void setOutputFunc(const OutputFunc_t& output) {
        s_output = output;
    }
    static
    void setOutputFunc(OutputFunc_t&& output) {
        s_output = std::move(output);
    }

    static
    void setFlushFunc(const FlushFunc_t& flush) {
        s_flush = flush;
    }
    static 
    void setFlushFunc(FlushFunc_t&& flush) {
        s_flush = std::move(flush);
    }


    LogPuter& operator<<(char);
    LogPuter& operator<<(short);
    LogPuter& operator<<(int);
    LogPuter& operator<<(long);
    LogPuter& operator<<(long long);
    LogPuter& operator<<(unsigned char);
    LogPuter& operator<<(unsigned short);
    LogPuter& operator<<(unsigned int);
    LogPuter& operator<<(unsigned long);
    LogPuter& operator<<(unsigned long long);

    LogPuter& operator<<(const char*);
    LogPuter& operator<<(const unsigned char*);
    LogPuter& operator<<(char*);
    LogPuter& operator<<(unsigned char*);

    template<typename T>
    LogPuter& operator<<(T* pointer){
        char buf[32];
        int sz = std::snprintf(buf, sizeof (buf), "%p", pointer);
        if (sz > 0 && m_buf.availableSize() >= m_reserveSize + sz) {
            m_buf.append(buf, static_cast<std::size_t>(sz));
        }
        return *this;
    }

    LogPuter& operator<<(const std::string&);
    template<std::size_t SZ>
    LogPuter& operator<<(const char[SZ]);




    
private:

    static
    void defaultOutput(const char* buf, std::size_t sz);

    static
    void defaultFlush();

    const char* m_fileName;
    std::size_t m_reserveSize;
    int m_line;
    LogLevel m_level;
    SmallBuffer m_buf;

    static const char* s_logLevelStr[static_cast<unsigned long>(LogLevel::nLogLevel)];
    static LogLevel s_level;
    static OutputFunc_t s_output;
    static FlushFunc_t s_flush;
    
};

#define LOG_DEBUG if (static_cast<int>(toym::LogPuter::getLevel()) <= static_cast<int>(toym::LogPuter::LogLevel::DEBUG)) \
                         toym::LogPuter(__FILE__, __LINE__, toym::LogPuter::LogLevel::DEBUG)
#define LOG_INFO if (static_cast<int>(toym::LogPuter::getLevel()) <= static_cast<int>(toym::LogPuter::LogLevel::INFO)) \
                         toym::LogPuter(__FILE__, __LINE__, toym::LogPuter::LogLevel::INFO)
#define LOG_WARNING if (static_cast<int>(toym::LogPuter::getLevel()) <= static_cast<int>(toym::LogPuter::LogLevel::WARNING)) \
                         toym::LogPuter(__FILE__, __LINE__, toym::LogPuter::LogLevel::WARNING)
#define LOG_ERROR if (static_cast<int>(toym::LogPuter::getLevel()) <= static_cast<int>(toym::LogPuter::LogLevel::ERROR)) \
                         toym::LogPuter(__FILE__, __LINE__, toym::LogPuter::LogLevel::ERROR)
#define LOG_CRITICAL toym::LogPuter(__FILE__, __LINE__, toym::LogPuter::LogLevel::CRITICAL)

} // namespace toym


#endif //__TOYM__BASE__LOGGER_H__